/*
Copyright (C) 2008, 2009 Henrik Naess

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Drawing;
using System.Collections.Generic;
using System.Globalization;
using System.Xml;
using MatrixPlugin.Constants;
using ZoneFiveSoftware.Common.Data.Fitness;
using ZoneFiveSoftware.Common.Visuals;

namespace MatrixPlugin
{
    class PositionGroupLabelProvider : TreeList.ILabelProvider
    {
        public Image GetImage(object element, TreeList.Column column)
        {
            return null;
        }
        public string GetText(object element, TreeList.Column column)
        {
            PositionGroup t = (PositionGroup)element;
            return t.Name;
        }
    }

    public class PositionGroup
    {
        #region Const and Privates

        private class xmlTags
        {
            public const string HideBlankColumnsAndRowsText = "hideblankcolumnsandrows";
            public const string StoppedText = "stopped";
            public const string TempoText = "tempo";
            public const string Default = "default";
            public const string name = "name";
            public const string data = "data";
        }
        private String _name;

        private bool _validDataValue = false;
        private General.DataValue _dataValue = General.DataValue.distance;
        private bool _validTempoType = false;
        private General.TempoType _tempoType = General.TempoType.Fastest;
        private bool? _includeStoppedTime;
        private bool? _hideBlankColumnsAndRows;
        private bool _isSplitsGroup = false;
        private IActivity _activity = null;
        private IList<MatrixPoint> _points;
        private ViewerController _viewerController = null;

        #endregion

        #region Public Properties

        public String Name
        {
            get { return _name; }
            //set { _name = value; }
        }

        public bool ValidDataValue
        {
            get { return _validDataValue; }
        }
        public General.DataValue DataValue
        {
            get { return _dataValue; }
            //set { _dataValue = value; }
        }

        public bool ValidTempoType
        {
            get { return _validTempoType; }
        }
        public General.TempoType TempoType
        {
            get { return _tempoType; }
            //set { _tempoType = value; }
        }

        public bool? IncludeStoppedTime
        {
            get { return _includeStoppedTime; }
            //set { _includeStoppedTime = value; }
        }

        public bool IsSplits
        {
            get { return _isSplitsGroup; }
        }
        public ViewerController Controller
        {
            get
            {
                return _viewerController;
            }
            set
            {
                _viewerController = value;
            }
        }

        public IList<MatrixPoint> MatrixPoints
        {
            get
            {
                //Need to get the points also for Splits group
                if (IsSplits)
                {
                    if (_viewerController == null || null == _viewerController.BaseActivity ||
                        null == _viewerController.BaseActivity.Laps ||
                        0 == _viewerController.BaseActivity.Laps.Count)
                    {
                        _points = null;
                    }
                    else if (null == _points || _viewerController.BaseActivity != _activity)
                    {
                        _activity = _viewerController.BaseActivity;
                        _points = new List<MatrixPoint>();
                        for (int j = 0; j < _activity.Laps.Count; j++)
                        {
                            try
                            {
                                //Malformed splits can cause exceptions
                                float lat = _activity.GPSRoute.GetInterpolatedValue(_activity.Laps[j].StartTime).Value.LatitudeDegrees;
                                float lng = _activity.GPSRoute.GetInterpolatedValue(_activity.Laps[j].StartTime).Value.LongitudeDegrees;
                                string name = CommonResources.Text.LabelSplits + (j + 1);
                                if (!String.IsNullOrEmpty(_activity.Laps[j].Notes))
                                {
                                    name += " (" + _activity.Laps[j].Notes + ")";
                                }
                                _points.Add(new MatrixPoint(name, lat, lng));
                            }
                            catch { }
                        }
                        //Use last position, also handles "ghost lap"
                        string ename = Properties.Resources.PosGroup_Splits_End;
                        try
                        {
                            _points.Add(new MatrixPoint(ename,
                                _activity.GPSRoute[_activity.GPSRoute.Count - 1].Value.LatitudeDegrees,
                                _activity.GPSRoute[_activity.GPSRoute.Count - 1].Value.LongitudeDegrees));
                        }
                        catch { }
                    }
                }
                return _points;
            }
        }

        public bool? HideBlankColumnsAndRows
        {
            get { return _hideBlankColumnsAndRows; }
            //set { _hideBlankColumnsAndRows = value; }
        }

        #endregion

        #region Constructors

        public PositionGroup(XmlNode node)
        {
            if (node != null)
                SetValues(node);
        }

        /// <summary>
        /// Create a position group. Uses values from default group if user hasn't specified attribute for this group
        /// </summary>
        /// <param name="node">xml node for position group</param>
        /// <param name="defaultGroup">default/basis position group</param>
        public PositionGroup(XmlNode node, PositionGroup defaultGroup)
        {
            if (node == null)
                return;

            SetValues(node);

            if (node.Attributes[xmlTags.name] != null)
                _name = node.Attributes[xmlTags.name].InnerText;
            else
                _name = defaultGroup.Name;

            //if (_dataValue == null) {
            //    _dataValue = defaultGroup.DataValue;

            //if (_hideBlankColumnsAndRows == null)
            //    _hideBlankColumnsAndRows = defaultGroup._hideBlankColumnsAndRows;

            //if (_includeStoppedTime == null)
            //    _includeStoppedTime = defaultGroup._includeStoppedTime;

            AddPoints(node.ChildNodes);
        }
        public PositionGroup(string name, IList<MatrixPoint> points)
        {
            _name = name;
            _points = points;
        }
        //PositionGroup added without points, activity must be set to calculate points
        public PositionGroup(string name, bool isSplits) 
        {
            _name = name;
            _points = null;
            this._isSplitsGroup = isSplits;
        }
        #endregion

        public void ClearCalculations()
        {
            if (_isSplitsGroup)
            {
                _points = null;
            }
            else
            {
                foreach (MatrixPoint point in MatrixPoints)
                    point.ClearCalculations();
            }
        }

        private void SetValues(XmlNode node)
        {
            if (node.Attributes[xmlTags.data] != null)
                _dataValue = (General.DataValue)Enum.Parse(typeof(General.DataValue), node.Attributes["data"].InnerText);

            if (node.Attributes[xmlTags.Default] != null)
                _name = node.Attributes[xmlTags.Default].InnerText;

            if (node.Attributes[xmlTags.TempoText] != null)
            {
                _tempoType = (General.TempoType)Enum.Parse(typeof(General.TempoType), node.Attributes[xmlTags.TempoText].InnerText);
            }

            SetAttributeIfFound(node, xmlTags.HideBlankColumnsAndRowsText, ref _hideBlankColumnsAndRows);
            SetAttributeIfFound(node, xmlTags.StoppedText, ref _includeStoppedTime);
        }

        private static void SetAttributeIfFound(XmlNode node, string attributeName, ref bool? fieldToUpdate)
        {
            if (node.Attributes[attributeName] != null)
            {
                string value = node.Attributes[attributeName].InnerText.ToLower();
                if (value == "true")
                    fieldToUpdate = true;
                else if (value == "false")
                    fieldToUpdate = false;
            }
        }

        private void AddPoints(XmlNodeList nodes)
        {
            _points = new List<MatrixPoint>(nodes.Count - 1);
            foreach (XmlNode node in nodes)
            {
                if (node.NodeType != XmlNodeType.Comment)
                {
                    string name = node.Attributes["name"].InnerText;

                    // Find Latitude and Longitude. Default separator is . (if this fail, use locale)
                    string latitudeText = node.Attributes["lat"].InnerText;
                    string longitudeText = node.Attributes["lon"].InnerText;
                    float latitude;
                    float longitude;
                    try
                    {
                        latitude = float.Parse(latitudeText, CultureInfo.CreateSpecificCulture("en-US"));
                        longitude = float.Parse(longitudeText, CultureInfo.CreateSpecificCulture("en-US"));
                    }
                    catch (FormatException)
                    {
                        latitude = float.Parse(latitudeText);
                        longitude = float.Parse(longitudeText);
                    }

                    MatrixPoint point = new MatrixPoint(name, latitude, longitude);
                    _points.Add(point);
                }
            }
        }
    }
}